from odoo import api, fields, models, _
from odoo.exceptions import ValidationError


class SaleChangeApply(models.Model):
    _name = 'sale.change.apply'
    _inherit = ['portal.mixin', 'mail.thread.main.attachment', 'mail.activity.mixin']

    def _default_name(self):
        return self.env['ir.sequence'].next_by_code('sale.change.apply')

    name = fields.Char(string='Ref', default=_default_name)
    # is_option = fields.Boolean(default=False, help='控制客户、客户部件不可更改')
    # ===========================第二版本，这些字段可能用不上=============================

    code = fields.Char(string='PN')
    change_type = fields.Selection([
        ('change', 'Current Change'),
        ('no_change', 'No Change'),
        ('next_change', 'Next Change')
    ], string='Change Type')
    # ===============================================================================
    approve_users = fields.Many2many('res.users', string='All Approve User', compute="_compute_approve_users",
                                     store=True)
    is_approve_user = fields.Boolean(default=False, compute='_compute_is_approve_user')
    is_approve_user_cancel = fields.Boolean(default=False)
    sb_workflow_template_id = fields.Many2one('sb.workflow.template', string="Approval Workflow Template",
                                              domain="[('sb_workflow_template_item_ids', '!=', False),"
                                                     "('workflow_type', '=', 'sale_change_apply')]")

    sb_workflow_id = fields.Many2one('sb.workflow', string="Current Approval Workflow", copy=False)
    sb_workflow_user = fields.Many2one('res.users', related='sb_workflow_id.create_uid', store=True,
                                       string='Apply User')
    sb_workflow_state = fields.Selection(related="sb_workflow_id.state", readonly=True, string="Approval Status",
                                         store=True)
    sb_workflow_item_ids = fields.One2many(related="sb_workflow_id.sb_workflow_item_ids", string="Approval Items")
    sb_workflow_attachments_ids = fields.One2many(related="sb_workflow_id.sb_workflow_attachments_ids",
                                                  string="Approval Attachments")
    sb_workflow_ids = fields.One2many('sb.workflow', 'sale_change_apply_id', string="Approval Workflows")
    sb_workflow_ids_sum = fields.Integer(compute="_compute_sb_workflow_ids_sum", store=True)

    sb_sale_change_ids = fields.One2many('sb.sale.change', 'change_apply_id')
    sb_version_change_ids = fields.One2many('sale.version.change', 'change_apply_id')
    pn = fields.Many2one('customer.item.code', string="PN")
    pn_code = fields.Char(related="pn.code", string="PN")
    version = fields.Char(related="pn.version", string="PN REV")
    pn_desc = fields.Char(related="pn.customer_item_desc", string="PN Desc")
    partner_id = fields.Many2one('res.partner', domain="[('code', '!=', False)]", string='Code')
    sale_order_id = fields.Many2one('sale.order', string='MO')
    apply_type = fields.Selection([
        ('change_info_01', 'Unit Price,NRE,PO'),
        ('change_info_02', 'PN,REV,QTY,Delivery Date,Note,QTA,ProStu,SoType'),
        ('change_status', 'Cancel/Hold/Restart/Split/Cancel balance'),
        ('change_design', 'Order design changes')
    ], string='Apply Type')
    order_type = fields.Selection([
        ('hold', 'Hold'),
        ('restart', 'Restart'),
        ('cancel', 'Cancel'),
        ('split', 'Split'),
        ('close', 'Cancel balance')
    ], string='Order Change')
    product_id = fields.Many2one('product.product', string='Customer Part')
    line_ids = fields.One2many('sale.change.apply.line', 'change_apply_id', string='Order Lines')
    note = fields.Text(string='Change Reason', tracking=True)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('approval', 'Under Approval'),
        ('approve', 'Approved'),
        ('fail_to_approve', 'Failed to Approve'),
        ('cancel', 'Cancel'),
        ('return', 'Return')
    ], default='draft', string='Status', tracking=True, compute="_compute_state", store=True)
    split_line_ids = fields.One2many("sale.split.apply.line", 'change_apply_id', string='Split Lines')
    product_tmpl_id = fields.Many2one('product.template', string="MPN")
    online = fields.Selection([('change', 'Change'), ('no_change', 'No change')], string='Online')
    inventory_mode = fields.Selection([('scrap', 'Scrap'), ('receive', 'Receive')], string='Inventory processing mode')

    # # 变更前字段 ===================
    o_nre = fields.Float(string='Origin NRE', digits='Sale Unit Price')
    o_price_unit = fields.Float('Origin Unit Price', digits='Sale Unit Price')
    o_po = fields.Char(string="Origin PO")
    o_note = fields.Html(string='Origin Notes')
    o_pn = fields.Many2one('customer.item.code', string="Origin PN")
    o_pn_code = fields.Char(string="Origin PN")
    o_qty = fields.Integer(string='Origin Order Qty')
    o_commitment_date = fields.Datetime(string='Origin Customer DD')
    # o_planned_receiving_date = fields.Datetime(string='Origin Factory DD')
    o_version = fields.Char(string="Origin PN REV")
    o_pn_desc = fields.Char(string="Origin PN Desc")
    o_product_tmpl_id = fields.Many2one('product.template', string='Origin Product')
    o_qt = fields.Boolean(string="Origin QTA")
    o_materiel_status = fields.Selection([
        ('S', 'NPI'),
        ('P', 'MP'),
        ('T', 'Test'),
        ('Q', 'QTA+NPI'),
    ], string='Origin Product Status', tracking=True)
    o_special_order_type = fields.Selection([
        ('eq_only', 'EQ only'),
        ('free_cost', 'Free cost'),
        ('in_stock', 'In stock'),
        ('wb', 'WB'),
        ('prepare_materials', 'Prepare Materials')
    ], string="Origin Special Order Type")
    # ============================

    ###=========可更改的字段===========
    c_nre = fields.Float(string='Target NRE', default=-1, digits='Sale Unit Price')
    c_price_unit = fields.Float('Target Unit Price', default=-1, digits='Sale Unit Price')
    c_po = fields.Char(string="Target PO")
    c_note = fields.Html(string='Target Notes')
    # c_pn = fields.Many2one('customer.item.code', string="Target PN")
    c_pn_code = fields.Char(string="Target PN")
    c_qty = fields.Integer(string='Target Order Qty')
    c_commitment_date = fields.Datetime(string='Target Customer DD')
    # c_planned_receiving_date = fields.Datetime(string='Target Factory DD')
    c_version = fields.Char(string="Target PN REV")
    c_pn_desc = fields.Char(string="Target PN Desc")
    c_product_tmpl_id = fields.Many2one('product.template', string='Target Product')
    c_qt = fields.Boolean(string='Target QTA')
    c_qt_select = fields.Selection([('None', 'None'), ('Yes', 'set True'), ('No', 'set false')], default='None',
                                   string='Select Target QTA')
    c_materiel_status = fields.Selection([
        ('S', 'NPI'),
        ('P', 'MP'),
        ('T', 'Test'),
        ('Q', 'QTA+NPI'),
    ], string='Target Product Status', tracking=True)
    c_special_order_type = fields.Selection([
        ('eq_only', 'EQ only'),
        ('free_cost', 'Free cost'),
        ('in_stock', 'In stock'),
        ('wb', 'WB'),
        ('prepare_materials', 'Prepare Materials'),
        ('nothing', 'Nothing')
    ], string="Target Special Order Type")
    # 判断对应字段是否更改
    is_c_nre = fields.Boolean(default=True)
    is_c_price_unit = fields.Boolean(default=True)
    is_c_po = fields.Boolean(default=True)
    is_c_note = fields.Boolean(default=True)
    is_c_pn = fields.Boolean(default=True)
    is_c_qty = fields.Boolean(default=True)
    # is_c_planned_receiving_date = fields.Boolean(default=True)
    is_c_version = fields.Boolean(default=True)
    is_c_pn_desc = fields.Boolean(default=True)
    is_c_product_tmpl_id = fields.Boolean(default=True)
    is_c_qt = fields.Boolean(default=True)
    is_c_materiel_status = fields.Boolean(default=True)
    is_c_special_order_type = fields.Boolean(default=True)
    is_c_commitment_date = fields.Boolean(default=True)
    ###==============================
    approve_progress = fields.Html(string='Approval Progress', compute='_compute_approve_progress')

    @api.depends('write_uid')
    def _compute_approve_progress(self):
        for rec in self:
            html_ = ""
            for itm in rec.sb_workflow_item_ids:
                if itm.state == 'approval':
                    p_ = """<p style='color:#E99F37'>{}-Approval</p>""".format(itm.name)
                elif itm.state == 'pass':
                    p_ = """<p style='color:green'>{}-Approved</p>""".format(itm.name)
                elif itm.state == 'no_pass':
                    p_ = """<p style='color:red'>{}-Refused</p>""".format(itm.name)
                elif itm.state == 'return':
                    p_ = """<p style='color:black'>{}-Return</p>""".format(itm.name)
                else:
                    p_ = ""
                if p_ and not html_:
                    html_ = p_
                elif p_ and html_:
                    html_ = html_ + "</br>" + p_
            rec.approve_progress = html_

    def onchange_is_c_options(self):
        if self.state != 'draft':
            if self.c_nre == -1 or self.o_nre == self.c_nre:
                self.is_c_nre = False
            if self.c_price_unit == -1 or self.o_price_unit == self.c_price_unit:
                self.is_c_price_unit = False
            if not self.c_po or self.o_po == self.c_po:
                self.is_c_po = False
            if not self.c_note or self.o_note == self.c_note:
                self.is_c_note = False
            if not self.c_pn_code or self.o_pn_code == self.c_pn_code:
                self.is_c_pn = False
            if not self.c_qty or self.o_qty == self.c_qty:
                self.is_c_qty = False
            # 不从此处发起修改计划交期，因此注释
            # if not self.c_planned_receiving_date :
            #     self.is_c_planned_receiving_date = False
            if not self.c_version or self.o_version == self.c_version:
                self.is_c_version = False
            if not self.c_pn_desc or self.o_pn_desc == self.c_pn_desc:
                self.is_c_pn_desc = False
            if not self.c_product_tmpl_id or \
                    (
                            self.o_product_tmpl_id and self.c_product_tmpl_id and self.o_product_tmpl_id.id == self.c_product_tmpl_id.id):
                self.is_c_product_tmpl_id = False
            if self.apply_type != 'change_info_02' or self.c_qt_select == 'None':
                self.is_c_qt = False
            if self.c_qt_select != 'None' and self.apply_type == 'change_info_02':
                self.is_c_qt = not (self.o_qt == self.c_qt) if self.o_qt is not None else False
            if not self.c_materiel_status or self.o_materiel_status == self.c_materiel_status:
                self.is_c_materiel_status = False
            if not self.c_special_order_type or self.o_special_order_type == self.c_special_order_type:
                self.is_c_special_order_type = False
            if not self.c_commitment_date or self.o_commitment_date == self.c_commitment_date:
                self.is_c_commitment_date = False

    @api.onchange('sale_order_id')
    def onchange_line_ids(self):
        self.line_ids = None
        domain = [('id', '=', self.sale_order_id.id)] if self.sale_order_id else []
        line_vals = []

        if domain:
            if self.order_type == 'restart':
                records = self.env['sale.order'].sudo().search(domain).filtered(lambda x: x.status in ['hold'])
            elif self.order_type in ['cancel', 'close']:
                records = self.env['sale.order'].sudo().search(domain).filtered(
                    lambda x: x.status in ['hold', 'active', 'part_completed'])
            else:
                records = self.env['sale.order'].sudo().search(domain).filtered(
                    lambda x: x.status in ['active', 'part_completed'])

            order_lines = records.mapped('order_line').filtered(lambda x: x.product_id.detailed_type != 'service')
            for line in order_lines:
                code_ids = self.env['customer.item.code'].search([('code', '=', line.pn)])
                nre = sum(line.order_id.order_line.filtered(lambda x: x.product_id.detailed_type == 'service').mapped(
                    'price_total'))
                pn = code_ids[0].id if code_ids else None

                line_vals.append((0, 0, {
                    'sale_order_id': line.order_id.id,
                    'order_line': line.id,
                    'commitment_date': line.order_id.commitment_date,
                    'product_tmpl_id': line.product_template_id.id,
                    'note': line.order_id.note,
                    'nre': nre,
                    'remain_qty': line.product_uom_qty,
                    'po': line.order_id.po,
                    'pn': pn,
                    'pn_code': code_ids[0].code if code_ids else None,
                    'version': code_ids[0].version if code_ids else '',
                    'pn_desc': code_ids[0].customer_item_desc if code_ids else '',
                    'qty': line.product_uom_qty,
                    'qty_delivered': line.qty_delivered,
                    'production_ids': [(3, prod.id) for prod in line.order_id.mrp_production_ids],
                    'production_qty': sum(
                        [prod.product_qty if prod else 0 for prod in line.order_id.mrp_production_ids]),
                    'price_unit': line.price_unit,
                    'production_state': 'done',
                }))
                self.update({
                    'o_nre': nre,
                    'o_price_unit': line.price_unit,
                    'o_po': line.order_id.po,
                    'o_note': line.order_id.note,
                    'o_pn': pn,
                    'o_pn_code': code_ids[0].code if code_ids else '',
                    'o_qty': line.product_uom_qty,
                    'o_commitment_date': line.order_id.commitment_date,
                    # 'o_planned_receiving_date': line.order_id.planned_receiving_date,
                    'o_version': code_ids[0].version if code_ids else '',
                    'o_pn_desc': code_ids[0].customer_item_desc if code_ids else '',
                    'o_product_tmpl_id': line.product_template_id.id,
                    'o_qt': line.s_qt,
                    'o_materiel_status': line.s_materiel_status,
                    'o_special_order_type': line.order_id.special_order_type,
                    'is_c_nre': True,
                    'is_c_price_unit': True,
                    'is_c_po': True,
                    'is_c_note': True,
                    'is_c_pn': True,
                    'is_c_qty': True,
                    # 'is_c_planned_receiving_date': True,
                    'is_c_version': True,
                    'is_c_pn_desc': True,
                    'is_c_product_tmpl_id': True,
                    'is_c_qt': True,
                    'is_c_materiel_status': True,
                    'is_c_special_order_type': True,
                    'is_c_commitment_date': True,
                })
            self.line_ids = line_vals

    @api.onchange('partner_id', 'pn', 'product_tmpl_id')
    def _onchange_production_id(self):
        if self.order_type == 'restart':
            domain = [('status', '=', 'hold')]
        else:
            domain = [('status', 'in', ['active'])]
        if self.partner_id:
            domain.append(('partner_id', '=', self.partner_id.id))
        if self.pn:
            domain.append(('pn', '=', self.pn))
        if self.product_tmpl_id:
            domain.append(('product_id', '=', self.product_tmpl_id.id))
        return {
            'domain': {
                'sale_order_id': domain
            }
        }

    @api.onchange('sale_order_id')
    def onchange_pn(self):
        if self.sale_order_id:
            self.partner_id = self.sale_order_id.partner_id.id
            rec = self.env['customer.item.code'].search([('code', '=', self.sale_order_id.pn)])
            self.pn = rec[0].id if rec else None
            self.product_tmpl_id = self.sale_order_id.product_id.id

    @api.onchange('apply_type', 'order_type')
    def onchange_sb_workflow_template_id(self):
        # 订单信息变更-市场内部处理
        xmlid_01 = self.env.ref('sb_order_changes.workflow_data_01')
        # 订单信息变更-工厂处理
        xmlid_02 = self.env.ref('sb_order_changes.workflow_data_02')
        # 订单状态变更-拆分
        xmlid_03 = self.env.ref('sb_order_changes.workflow_data_03')
        # 订单状态变更-暂停
        xmlid_04 = self.env.ref('sb_order_changes.workflow_data_04')
        # 订单状态变更-重启
        xmlid_05 = self.env.ref('sb_order_changes.workflow_data_05')
        # 订单状态变更-取消
        xmlid_06 = self.env.ref('sb_order_changes.workflow_data_06')
        # 订单状态变更-关闭
        xmlid_07 = self.env.ref('sb_order_changes.workflow_data_07')
        # 订单设计变更
        xmlid_08 = self.env.ref('sb_order_changes.workflow_data_08')
        if self.apply_type == 'change_info_01':
            self.sb_workflow_template_id = xmlid_01
        elif self.apply_type == 'change_info_02':
            self.sb_workflow_template_id = xmlid_02
        elif self.apply_type == 'change_status' and self.order_type == 'split':
            self.sb_workflow_template_id = xmlid_03
        elif self.apply_type == 'change_status' and self.order_type == 'hold':
            self.sb_workflow_template_id = xmlid_04
        elif self.apply_type == 'change_status' and self.order_type == 'restart':
            self.sb_workflow_template_id = xmlid_05
        elif self.apply_type == 'change_status' and self.order_type == 'cancel':
            self.sb_workflow_template_id = xmlid_06
        elif self.apply_type == 'change_status' and self.order_type == 'close':
            self.sb_workflow_template_id = xmlid_07
        elif self.apply_type == 'change_design':
            self.sb_workflow_template_id = xmlid_08
        else:
            pass

    @api.onchange('c_qt_select')
    def onchange_c_qt_select(self):
        if self.c_qt_select == 'Yes':
            self.c_qt = True
        elif self.c_qt_select == 'No':
            self.c_qt = False

    @api.depends('sb_workflow_state', 'sb_workflow_item_ids')
    def _compute_state(self):
        for rec in self:
            if rec.is_approve_user_cancel:
                rec.state = 'cancel'
            elif rec.sb_workflow_state == 'approval':
                rec.state = 'approval'
            elif rec.sb_workflow_state == 'approve':
                rec.state = 'approve'
            elif rec.sb_workflow_state == 'fail_to_approve':
                rec.state = 'fail_to_approve'
            elif rec.sb_workflow_state == 'return':
                rec.state = 'return'
            else:
                rec.state = 'draft'

    @api.depends('sb_workflow_ids')
    def _compute_sb_workflow_ids_sum(self):
        for item in self:
            if item.sb_workflow_ids:
                item.sb_workflow_ids_sum = len(item.sb_workflow_ids)
            else:
                item.sb_workflow_ids_sum = 0

    def create_workflow(self):
        # if self.sale_order_id.sb_workflow_state == 'approval' and self.apply_type in ['change_status', 'change_design']:
        #     raise ValidationError(_('Unapproved MO cannot be Change'))

        if self.sb_workflow_template_id:
            if len(self.sb_workflow_ids) > 0:
                workflow_sum = len(self.sb_workflow_ids) + 1
            else:
                workflow_sum = 1

            if self.apply_type == 'change_info_01':
                if self.c_nre < 0 or self.c_price_unit < 0:
                    raise ValidationError(
                        _('The modify value should not be smaller than 0. Please enter the correct Target NRE and Target Unit Price.'))

            is_c_qt = False
            if self.c_qt_select != 'None' and self.apply_type == 'change_info_02':
                is_c_qt = not (self.o_qt == self.c_qt) if self.o_qt is not None else False
            if ((self.c_nre == -1 or self.o_nre == self.c_nre)
                    and (self.c_price_unit == -1 or self.o_price_unit == self.c_price_unit)
                    and (not self.c_po or self.o_po == self.c_po)

                    and (not self.c_note or self.o_note == self.c_note)
                    and (not self.c_pn_code or self.o_pn_code == self.c_pn_code)
                    and (not self.c_qty or self.o_qty == self.c_qty)
                    and (not self.c_version or self.o_version == self.c_version)
                    and (not self.c_pn_desc or self.o_pn_desc == self.c_pn_desc)
                    and (not self.c_product_tmpl_id or
                         (self.o_product_tmpl_id and self.c_product_tmpl_id
                          and self.o_product_tmpl_id.id == self.c_product_tmpl_id.id))
                    and not is_c_qt
                    and (not self.c_materiel_status or self.o_materiel_status == self.c_materiel_status)
                    and (not self.c_special_order_type or self.o_special_order_type == self.c_special_order_type)
                    and (not self.c_commitment_date or self.o_commitment_date == self.c_commitment_date)):
                raise ValidationError(_('Error: You must add at least one modify field before submit.'))

            if not self.sb_workflow_template_id.sb_workflow_template_item_ids:
                raise ValidationError(_('Please configure the approver first！'))
            # document_ids = self.env['ir.attachment'].search([('res_model', '=', self._name), ('res_id', '=', self.id)])

            # 创建审批流
            new_wf_id = self.env['sb.workflow'].sudo().create({
                'name': self.name + '- Approval Flow' + '-' + str(workflow_sum),
                'approval_time': fields.Datetime.now(),
                'workflow_type': 'sale_change_apply',
                'sale_change_apply_id': self.id,
                'res_model': self._name,
                'res_id': self.id,
                # 'document_ids': [(6, 0, document_ids.ids)]
            })
            self.sb_workflow_id = new_wf_id.id
            # 根据模板生成审批项
            item_audit_levels = []
            for item in self.sb_workflow_template_id.sb_workflow_template_item_ids:
                new_item = self.env['sb.workflow.item'].sudo().create({
                    'name': item.name,
                    'user_group_ids': item.user_group_ids.ids,
                    'audit_level': item.audit_level,
                    'sb_workflow_id': new_wf_id.id,
                    'state': 'wait_forward'
                })
                # 收集生成的审核项的所有层级
                item_audit_levels.append(new_item.audit_level)
            # 去重排序
            item_audit_levels = list(set(item_audit_levels))
            item_audit_levels.sort()
            # 开放最低层级的神匹配
            for n_item in new_wf_id.sb_workflow_item_ids:
                if n_item.audit_level == item_audit_levels[0]:
                    n_item.state = 'approval'
        else:
            raise ValidationError(_('Please select an approval template.'))

        self.update_line()

        if self.apply_type == 'change_status' and self.order_type == 'split':
            if self.line_ids.filtered(lambda x: x.remain_qty):
                # 拆分数量必须和订单总数一致
                raise ValidationError(_('The split quantity must match the total number of orders'))
            if not self.split_line_ids:
                # 请添加拆分明细
                raise ValidationError(_('Please add splitting details.'))
        self.onchange_is_c_options()

    def update_line(self):
        line = self.line_ids[0]
        if self.apply_type == 'change_info_01':
            if self.c_price_unit >= 0:
                line.c_price_unit = self.c_price_unit
            if self.c_nre >= 0:
                line.c_nre = self.c_nre
            if self.c_po:
                line.c_po = self.c_po
        elif self.apply_type == 'change_info_02':
            if self.c_pn_code:
                line.c_pn_code = self.c_pn_code
            if self.c_version:
                line.c_version = self.c_version
            if self.c_pn_desc:
                line.c_pn_desc = self.c_pn_desc
            if self.c_qty:
                line.c_qty = self.c_qty
            # if self.c_planned_receiving_date:
            #     line.c_planned_receiving_date = self.c_planned_receiving_date
            if self.c_note:
                line.c_note = self.c_note
            if self.is_c_qt:
                line.c_qt = self.c_qt
            if self.c_materiel_status:
                line.c_materiel_status = self.c_materiel_status
            if self.c_special_order_type:
                line.c_special_order_type = self.c_special_order_type
            if self.c_commitment_date:
                line.c_commitment_date = self.c_commitment_date

        elif self.apply_type == 'change_design':
            if self.c_product_tmpl_id:
                line.c_product_tmpl_id = self.c_product_tmpl_id
            if self.c_pn_code:
                line.c_pn_code = self.c_pn_code
            if self.c_version:
                line.c_version = self.c_version

    def _lock_order(self):
        # 锁定销售单、发货的、工单、报废、过数等
        order_lines = set()
        picking_ids = set()
        mrp_prods = set()
        lock_reason = self._lock_reason("ecr申请信息，申请人:{}, 申请原因：{}".format(self.create_uid.name, self.note))
        # 锁定销售单
        for line in self.line_ids:
            line.sale_order_id.is_lock = True
            line.sale_order_id.lock_reason = lock_reason
            if not order_lines:
                order_lines = line.sale_order_id.order_line
            else:
                order_lines |= line.sale_order_id.order_line
            if not picking_ids:
                picking_ids = line.sale_order_id.picking_ids
            else:
                picking_ids |= line.sale_order_id.picking_ids

        for line in order_lines:
            for mrp in line.s_mrp_production_ids:
                if not mrp_prods:
                    mrp_prods = mrp
                mrp_prods |= self._get_parent_mrp(mrp)
                mrp_prods |= self._get_child_mrp(mrp)
                picking_ids |= mrp.picking_ids

        # 锁定工单
        for mrp in mrp_prods:
            mrp.filtered(lambda x: x.state != 'done').is_lock = True
            mrp.filtered(lambda x: x.state != 'done').lock_reason = lock_reason
            # mrp.is_lock = True
        # 锁定picking(出库和生产)
        for picking in picking_ids:
            picking.filtered(lambda x: x.state != 'done').is_lock = True
            picking.filtered(lambda x: x.state != 'done').lock_reason = lock_reason
            # picking.is_lock = True

    def _get_parent_mrp(self, mrp):
        parent_ids = mrp._get_sources()
        # 欠单
        mrp_backorders = mrp.procurement_group_id.mrp_production_ids
        return parent_ids | mrp_backorders | mrp

    def _get_child_mrp(self, mrp):
        child_ids = mrp._get_children()
        # 欠单
        mrp_backorders = mrp.procurement_group_id.mrp_production_ids
        return child_ids | mrp_backorders

    def _lock_reason(self, str=''):
        return """<p style="color: red;">{}</p>""".format(str)

    @api.onchange('product_tmpl_id')
    def onchange_product_info(self):
        # print(self.product_tmpl_id.customer_item_code_ids)
        if not self.product_tmpl_id.customer_item_code_ids:
            return

        customer_item_code_ids = self.product_tmpl_id.customer_item_code_ids
        self.version = customer_item_code_ids[0].version
        self.code = customer_item_code_ids[0].code

    def search_action(self):
        context = dict(self._context)
        domain = [('status', 'in', ['hold', 'active']),
                  ('is_lock', '!=', True),
                  ('product_tmpl_id', '=', self.product_tmpl_id.id),
                  ('partner_id', '=', self.partner_id.id)]
        orders = self.env['sale.order'].sudo().search(domain)
        line_vals = []
        for o in orders:
            line_vals.append((0, 0, {
                'sale_order_id': o.id,
                'order_line': o.order_line[0].id,
            }))
        context['default_line_ids'] = line_vals
        context['default_partner_id'] = self.partner_id.id
        context['default_product_tmpl_id'] = self.product_tmpl_id.id
        context['default_sale_change_apply_id'] = self.id
        return {
            'name': _('Sale Change Apply'),
            'type': 'ir.actions.act_window',
            'res_model': 'sale.change.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': context
        }

    def _compute_is_approve_user(self):
        self.ensure_one()
        is_approve_user = False
        if self.env.uid in self.approve_users.ids:
            for item in self.sb_workflow_item_ids:
                if item.state == 'approval' and self.env.uid in item.user_group_ids.ids:
                    is_approve_user = True
                    continue
        self.is_approve_user = is_approve_user

    @api.depends('sb_workflow_id.state', 'sb_workflow_id')
    def _compute_approve_users(self):
        user_vals = []
        for item in self.sb_workflow_item_ids:
            for user in item.user_group_ids:
                user_vals.append((4, user.id))
        self.approve_users = user_vals

    # 审批
    def make_agree(self):
        item_id = None
        for item in self.sb_workflow_item_ids:
            if self.env.uid in item.user_group_ids.ids and item.state == 'approval':
                item_id = item
                break
        if not item_id:
            raise ValidationError(_(''))
        form_id = self.env.ref('sb_workflow.workflow_check_wizard_form').id
        wizard = self.env['workflow.check.wizard'].create([{'sb_workflow_item_id': item.id,
                                                            'planned_receiving_date': False,
                                                            'workflow_type': self.sb_workflow_id.workflow_type}])
        context = dict(self._context)
        context['ecr_return'] = True
        return {
            'name': _('Undergoing Approval'),
            'view_mode': 'form',
            'res_model': 'workflow.check.wizard',
            'res_id': wizard.id,
            'view_id': form_id,
            'target': 'new',
            'type': 'ir.actions.act_window',
            'context': context
        }

    def create_cache(self, vals):
        self.env['sb.order.cache'].create(vals)

    def action_cancel(self):
        if self.state in ['draft', 'approval']:
            self.is_approve_user_cancel = True
            self.mapped('sb_workflow_item_ids').write({'approval_instructions': '提交人员取消申请', 'state': 'no_pass'})
        else:
            raise ValidationError(_('当前状态不可取消'))

    def unlink(self):
        for rec in self:
            if rec.state != 'draft':
                raise ValidationError(_('当前单据状态不能删除'))
        return super(SaleChangeApply, self).unlink()

    @api.constrains('state')
    def constrains_state(self):
        for rec in self:
            if rec.state != 'approve':
                continue

            # 重启, 解锁
            if rec.order_type == 'restart':
                rec.action_order_unlock()

    def action_order_unlock(self):
        for rec in self:
            for line in rec.line_ids:
                sale_id = line.sale_order_id.sudo()
                sale_id.action_unlock()
                sale_id.action_cancel_suspend_order()
                sale_id.write({'status': 'active', 'is_lock': False})
                production_ids = line.sale_order_id.order_line.mapped('s_mrp_production_ids')
                for production in production_ids:
                    all_production = production._get_all_child() + production
                    all_production.sudo().write({'is_lock': False})

    def action_workflow(self):
        return {
            'name': _('Approval Flow'),
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'form',
            'res_id': self.sb_workflow_id.id,
            # 'views': [(self.env.ref('').id, 'form')],
            'target': 'current',
        }

    def action_view_workflow(self):
        return {
            'name': _('Approval Flow'),
            'type': 'ir.actions.act_window',
            'res_model': 'sb.workflow',
            'view_mode': 'tree,form',
            'target': 'current',
            'domain': [('sale_change_apply_id', '=', self.id)],
        }


class SbSaleChangeApplyLine(models.Model):
    _name = 'sale.change.apply.line'

    name = fields.Char(related="change_apply_id.name")
    change_apply_id = fields.Many2one('sale.change.apply')
    # 交货日期
    commitment_date = fields.Datetime(string="Commitment Date")
    sale_order_id = fields.Many2one('sale.order', string='M0')
    order_line = fields.Many2one('sale.order.line', string='Sale Order Line')
    mpn_id = fields.Many2one('product.template', compute="_compute_mpn_id", string="MPN", store=True)
    product_tmpl_id = fields.Many2one('product.template', string='Product')
    po = fields.Char(string="PO")
    pn = fields.Many2one('customer.item.code', string="PN")
    pn_code = fields.Char(string="PN")
    version = fields.Char(string="REV")
    pn_desc = fields.Char(string="PN Desc")
    qty = fields.Float(string='Order Qty')
    qty_delivered = fields.Float(string='Delivered Qty')
    # 剩余数量
    qty_remain = fields.Float(string='Remain Qty', compute="_compute_qty_remain")
    production_ids = fields.Many2many('mrp.production', 'apply_line_id', 'production_id')
    production_qty = fields.Float(string='Production Qty')
    price_unit = fields.Float('Unit Price')
    # ner 字段??
    production_state = fields.Selection([('done', 'Done')], string='Production Status')
    status = fields.Selection(related='sale_order_id.status', string='Status')
    # planned_receiving_date = fields.Datetime(string='Factory DD', related='sale_order_id.planned_receiving_date')
    note = fields.Html(string='Notes')
    nre = fields.Float(string='NRE', digits='Sale Unit Price')
    c_nre = fields.Float(string='Target NRE', digits='Sale Unit Price', default=-1)
    c_price_unit = fields.Float('Target Unit Price', digits='Sale Unit Price', default=-1)
    c_po = fields.Char(string="Target PO")
    c_note = fields.Html(string='Target Notes')
    # c_pn = fields.Many2one('customer.item.code', string="Target PN")
    c_pn_code = fields.Char(string="Target PN")
    c_pn_desc = fields.Char(string="Target PN Desc")
    c_qty = fields.Integer(string='Target Order Qty')
    c_commitment_date = fields.Datetime(string='Target Customer DD')
    # c_planned_receiving_date = fields.Datetime(string='Target Factory DD')
    c_version = fields.Char(string="Target REV")
    c_product_tmpl_id = fields.Many2one('product.template', string='Target Product')
    end_pn = fields.Char(related='sale_order_id.end_pn', string="End PN")
    end_pn_rev = fields.Char(related='sale_order_id.end_pn_rev', string='End Rev')
    end_po = fields.Char(related='sale_order_id.end_po', string='End PO')
    special_order_type = fields.Selection(related='sale_order_id.special_order_type',
                                          string="Origin Special Order Type")
    po_line = fields.Char(related='order_line.po_line', string='PO Line')
    s_qt = fields.Boolean(related='order_line.s_qt', string='QTA')
    s_materiel_status = fields.Selection(related='order_line.s_materiel_status', string='Product Status')
    remain_qty = fields.Float(string='Remaining Split Qty')
    split_line_ids = fields.One2many("sale.split.apply.line", 'apply_line_id', string='Split Lines')
    c_qt = fields.Boolean(string='Target QTA')
    c_materiel_status = fields.Selection([
        ('S', 'NPI'),
        ('P', 'MP'),
        ('T', 'Test'),
        ('Q', 'QTA+NPI'),
    ], string='Target Product Status', tracking=True)
    c_special_order_type = fields.Selection([
        ('eq_only', 'EQ only'),
        ('free_cost', 'Free cost'),
        ('in_stock', 'In stock'),
        ('prepare_materials', 'Prepare Materials'),
        ('nothing', 'Nothing')
    ], string="Target Special Order Type")

    @api.depends('pn')
    def _compute_mpn_id(self):
        if self.pn:
            self.mpn_id = self.pn.product_id[0].id
        else:
            self.mpn_id = False

    @api.depends('qty', 'qty_delivered')
    def _compute_qty_remain(self):
        for record in self:
            record.qty_remain = record.qty - record.qty_delivered

    @api.onchange('split_line_ids')
    def onchange_remain_qty(self):
        remain_qty = self.qty
        remain_qty -= sum([line.split_qty for line in self.split_line_ids])
        if remain_qty < 0:
            # 最大拆分数量不能超过 {}
            raise ValidationError('The maximum number of splits cannot exceed {}'.format(self.qty))
        self.remain_qty = remain_qty

    def look_info(self):
        view_id = self.env.ref('sb_order_changes.sale_change_apply_line_form_view').id
        return {
            'name': _('Look Info'),
            'type': 'ir.actions.act_window',
            'res_model': 'sale.change.apply.line',
            'view_mode': 'form',
            'target': 'new',
            'res_id': self.id,
            'views': [[view_id, 'form']],
        }


class SaleSplitApplyLine(models.Model):
    _name = "sale.split.apply.line"

    # 不从此处发起修改计划交期，因此注释
    # def _default_planned_date(self):
    #     return self.apply_line_id.planned_receiving_date

    apply_line_id = fields.Many2one('sale.change.apply.line')
    change_apply_id = fields.Many2one('sale.change.apply', related="apply_line_id.change_apply_id")
    change_line_id = fields.Many2one('sb.sale.change.line')
    split_qty = fields.Integer(string='Qty')
    sale_order_id = fields.Many2one('sale.order', related="apply_line_id.sale_order_id", string='M0')
    order_line = fields.Many2one('sale.order.line', related="apply_line_id.order_line", string='Sale Order Line')
    mpn_id = fields.Many2one('product.template', related="apply_line_id.mpn_id", string="MPN", store=True)
    po = fields.Char(string="PO", related="apply_line_id.po")
    pn = fields.Many2one('customer.item.code', related="apply_line_id.pn", string="PN")
    version = fields.Char(related="apply_line_id.version", string="REV")
    # c_planned_receiving_date = fields.Datetime(string='Factory DD', default=_default_planned_date)
    qty = fields.Float(related='order_line.product_uom_qty', string='Order Total Qty')

    @api.onchange('split_qty')
    def onchange_apply(self):
        self.change_apply_id = self.apply_line_id.change_apply_id.id
